#include <string.h>
#include <stdio.h>
#include "air105.h"

/* Private function prototypes -----------------------------------------------*/
#ifdef __GNUC__
/* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
   set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */

void NVIC_Configuration(void); // 中断初始化
void SPI_Configuration(void);  // SPI初始化
void UART_Configuration(void); // 串口初始化
void DMA_Configuration(void);  // DMA初始化
void DataPrintf(uint8_t *buf, uint32_t bufsize);// 打印数据

#define SEND_BUF_SIZE 256
#define RECE_BUF_SIZE 256

uint8_t send_buf[SEND_BUF_SIZE] = {0x00};
uint8_t rece_buf[RECE_BUF_SIZE] = {0x00};

uint32_t send_buf_index = 0;
uint32_t rece_buf_index = 0;

SPI_TypeDef *SPIx = SPIM2;

int main(void)
{
    uint32_t i;
    SYSCTRL_ClocksTypeDef clocks;
    SYSCTRL_PLLConfig(SYSCTRL_PLL_204MHz);                                                                        // 设置PLL时钟
    SYSCTRL_GetClocksFreq(&clocks);                                                                               // 获取时钟频率
    SYSCTRL_APBPeriphClockCmd(SYSCTRL_APBPeriph_UART0 | SYSCTRL_APBPeriph_GPIO | SYSCTRL_APBPeriph_SPI2, ENABLE); // 使能UART0和GPIO时钟
    SYSCTRL_APBPeriphResetCmd(SYSCTRL_APBPeriph_UART0 | SYSCTRL_APBPeriph_SPI2, ENABLE);                          // 复位UART0和SPI2
    SYSCTRL_AHBPeriphClockCmd(SYSCTRL_AHBPeriph_DMA, ENABLE);                                                     // 使能DMA时钟
    SYSCTRL_AHBPeriphResetCmd(SYSCTRL_AHBPeriph_DMA, ENABLE);                                                     // 复位DMA
    NVIC_Configuration();                                                                                         // 中断初始化
    UART_Configuration();                                                                                         // 串口初始化
    printf("LuatOS AIR105 SPI Master Tx And Rx Demo V1.0.\n");
    printf("短接SPI2的MISO和MOSI，即可看到数据发送和接收\n");
    printf("CPU_Frequency:%d,\nHCLK_Frequency:%d,\nPCLK_Frequency:%d,\nPLL_Frequency:%d\n\r", clocks.CPU_Frequency / 1000000, clocks.HCLK_Frequency / 1000000, clocks.PCLK_Frequency / 1000000, clocks.PLL_Frequency / 1000000);

    // Send Data Init 0x50 To 0x5F
    printf("初始发送数据\n");
    for (i = 0; i < SEND_BUF_SIZE; i++)
    {
        send_buf[i] = (uint8_t)((i & 0x0F) | 0x50);
    }

    printf("发送Buf为：\n");
    DataPrintf(send_buf, SEND_BUF_SIZE);
    printf("接收Buf\n");
    DataPrintf(rece_buf, RECE_BUF_SIZE);
    SPI_Configuration(); // SPI初始化
    DMA_Configuration(); // DMA初始化

    // Clear RX FIFO
    printf("清空接收\n");
    while (SPI_GetFlagStatus(SPIx, SPI_FLAG_RXNE)) // SPI接收缓冲区非空
        SPI_ReceiveData(SPIx);                     // 读取SPI接收缓冲区数据

    printf("发送开始\n\n");

    for (i = 0; i < (SEND_BUF_SIZE / 8); i++)
    {
        while (DMA_IsChannelEnabled(DMA_Channel_0) != DISABLE) // DMA通道0使能
            ;
        DMA_ChannelCmd(DMA_Channel_0, ENABLE); // 使能DMA通道0
    }
    printf("发送结束\n");

    printf("发送数据为：\n");
    DataPrintf(send_buf, SEND_BUF_SIZE);
    printf("接收数据为：\n");
    DataPrintf(rece_buf, RECE_BUF_SIZE);
    // 校验数据
    for (i = 0; i < SEND_BUF_SIZE; i++)
    {
        if (send_buf[i] != rece_buf[i])
        {
            printf("数据错误，发送数据为：0x%02X，接收数据为：0x%02X，错误位置为：%d\n", send_buf[i], rece_buf[i], i);
            break;
        }
    }
    if (i == SEND_BUF_SIZE)
        printf("数据正确\n");
    while (1)
        ;
}

void NVIC_Configuration(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;

    NVIC_SetPriorityGrouping(NVIC_PriorityGroup_3);

    NVIC_InitStructure.NVIC_IRQChannel = DMA_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

void SPI_Configuration(void)
{
    SPI_InitTypeDef SPI_InitStructure;
    SPI_DMAInitTypeDef SPI_DMAInitStructure;

    // SPI2
    GPIO_PinRemapConfig(GPIOB, GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5, GPIO_Remap_0);

    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;       // SPI配置为双线全双工
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;                        // SPI发送接收8位帧结构
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;                               // 时钟悬空低
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;                             // 数据捕获于第1个时钟沿
    SPI_InitStructure.SPI_NSS = SPI_NSS_0;                                   // NSS信号由硬件(SSI)管理
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;     // 波特率预分频值为256
    SPI_InitStructure.SPI_RXFIFOFullThreshold = SPI_RXFIFOFullThreshold_8;   // 接收FIFO阈值为8
    SPI_InitStructure.SPI_TXFIFOEmptyThreshold = SPI_TXFIFOEmptyThreshold_7; // 发送FIFO阈值为7

    SPI_DMAInitStructure.SPI_DMAReq = SPI_DMAReq_Tx | SPI_DMAReq_Rx;    // SPI DMA发送接收请求
    SPI_DMAInitStructure.SPI_DMATransmitLevel = SPI_DMATransmitLevel_0; // SPI DMA发送阈值为0
    SPI_DMAInitStructure.SPI_DMAReceiveLevel = SPI_DMAReceiveLevel_1;   // SPI DMA接收阈值为1
    SPI_DMAInitStructure.SPI_DMAEnCmd = ENABLE;                         // SPI DMA使能

    SPI_Init(SPIx, &SPI_InitStructure);
    SPI_DMAInit(SPIx, &SPI_DMAInitStructure);
    SPI_Cmd(SPIx, ENABLE);
}

void DMA_Configuration(void)
{
    DMA_InitTypeDef DMA_InitStruct;

    // memory to SPI0
    DMA_InitStruct.DMA_Peripheral = (uint32_t)(SPIx);                          // DMA外设地址
    DMA_InitStruct.DMA_DIR = DMA_DIR_Memory_To_Peripheral;                     // DMA传输方向，内存到外设
    DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&SPIx->DR;               // DMA外设基地址
    DMA_InitStruct.DMA_PeripheralInc = DMA_Inc_Nochange;                       // DMA外设地址不变
    DMA_InitStruct.DMA_PeripheralDataSize = DMA_DataSize_Byte;                 // DMA外设数据宽度为8位
    DMA_InitStruct.DMA_PeripheralBurstSize = DMA_BurstSize_4;                  // DMA外设突发传输大小为4
    DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)&send_buf[0];                // DMA内存基地址
    DMA_InitStruct.DMA_MemoryInc = DMA_Inc_Increment;                          // DMA内存地址递增
    DMA_InitStruct.DMA_MemoryDataSize = DMA_DataSize_Byte;                     // DMA内存数据宽度为8位
    DMA_InitStruct.DMA_MemoryBurstSize = DMA_BurstSize_4;                      // DMA内存突发传输大小为4
    DMA_InitStruct.DMA_BlockSize = 8;                                          // DMA传输块大小为8
    DMA_InitStruct.DMA_PeripheralHandShake = DMA_PeripheralHandShake_Hardware; // DMA外设硬件请求
    DMA_Init(DMA_Channel_0, &DMA_InitStruct);                                  // DMA通道0初始化

    // SPI0 to memory
    DMA_InitStruct.DMA_Peripheral = (uint32_t)(SPIx);                          // DMA外设地址
    DMA_InitStruct.DMA_DIR = DMA_DIR_Peripheral_To_Memory;                     // DMA传输方向，外设到内存
    DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&SPIx->DR;               // DMA外设基地址
    DMA_InitStruct.DMA_PeripheralInc = DMA_Inc_Nochange;                       // DMA外设地址不变
    DMA_InitStruct.DMA_PeripheralDataSize = DMA_DataSize_Byte;                 // DMA外设数据宽度为8位
    DMA_InitStruct.DMA_PeripheralBurstSize = DMA_BurstSize_1;                  // DMA外设突发传输大小为1
    DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)&rece_buf[0];                // DMA内存基地址
    DMA_InitStruct.DMA_MemoryInc = DMA_Inc_Increment;                          // DMA内存地址递增
    DMA_InitStruct.DMA_MemoryDataSize = DMA_DataSize_Byte;                     // DMA内存数据宽度为8位
    DMA_InitStruct.DMA_MemoryBurstSize = DMA_BurstSize_1;                      // DMA内存突发传输大小为1
    DMA_InitStruct.DMA_BlockSize = 1;                                          // DMA传输块大小为1
    DMA_InitStruct.DMA_PeripheralHandShake = DMA_PeripheralHandShake_Hardware; // DMA外设硬件请求
    DMA_Init(DMA_Channel_1, &DMA_InitStruct);                                  // DMA通道1初始化
    DMA_ITConfig(DMA_Channel_1, DMA_IT_BlockTransferComplete, ENABLE);         // DMA通道1传输完成中断使能
    DMA_ChannelCmd(DMA_Channel_1, ENABLE);                                     // DMA通道1使能
    DMA_Cmd(ENABLE);                                                           // DMA使能
}

void UART_Configuration(void)
{
    UART_InitTypeDef UART_InitStructure;

    GPIO_PinRemapConfig(GPIOA, GPIO_Pin_0 | GPIO_Pin_1, GPIO_Remap_0);

    UART_InitStructure.UART_BaudRate = 115200;
    UART_InitStructure.UART_WordLength = UART_WordLength_8b;
    UART_InitStructure.UART_StopBits = UART_StopBits_1;
    UART_InitStructure.UART_Parity = UART_Parity_No;

    UART_Init(UART0, &UART_InitStructure);
}

void DataPrintf(uint8_t *buf, uint32_t bufsize)
{
    uint32_t i = 0;
    if (0 != bufsize)
    {
        for (i = 0; i < bufsize; i++)
        {
            if (0 != i && 0 == i % 16)
                printf("\n");
            printf("%02X", buf[i]);
        }
    }
    printf("\n\n");
}

// Retarget Printf
int SER_PutChar(int ch)
{
    while (!UART_IsTXEmpty(UART0))
        ;
    UART_SendData(UART0, (uint8_t)ch);

    return ch;
}

int fputc(int c, FILE *f)
{
    /* Place your implementation of fputc here */
    /* e.g. write a character to the USART */
    if (c == '\n')
    {
        SER_PutChar('\r');
    }
    return (SER_PutChar(c));
}

#ifdef USE_FULL_ASSERT

/**
 * @brief  Reports the name of the source file and the source line number
 *         where the assert_param error has occurred.
 * @param  file: pointer to the source file name
 * @param  line: assert_param error line source number
 * @retval None
 */
void assert_failed(uint8_t *file, uint32_t line)
{
    /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

    /* Infinite loop */
    while (1)
    {
    }
}
#endif
